/*
 * Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifdef LOSCFG_KERNEL_CONTAINER

#include "los_container_bundle.h"
#ifdef LOSCFG_MNT_CONTAINER
#include "los_mnt_container.h"
#endif
#include "los_uts_container.h"
#ifdef LOSCFG_NET_CONTAINER
#include "los_net_container.h"
#endif
#ifdef LOSCFG_PID_CONTAINER
#include "los_pid_container.h"
#endif
#ifdef LOSCFG_TIME_CONTAINER
#include "los_time_container.h"
#endif
#ifdef LOSCFG_IPC_CONTAINER
#include "los_ipc_container.h"
#endif
#include <linux/module.h>

const int BUNDLE_INIT_NUM = 3;

ContainerBundle g_rootContainerBundle = {
    .count           = ATOMIC_INIT(BUNDLE_INIT_NUM),
#ifdef LOSCFG_UTS_CONTAINER
    .utsContainer    = &g_rootUtsContainer,
#endif
#ifdef LOSCFG_MNT_CONTAINER
    .mntContainer    = &g_rootMntContainer,
#endif
#ifdef LOSCFG_NET_CONTAINER
    .netContainer    = &g_rootNetContainer,
#endif
#ifdef LOSCFG_PID_CONTAINER
    .pidContainer    = &g_rootPidContainer,
    .pidContainerForChildren = &g_rootPidContainer,
#endif
#ifdef LOSCFG_TIME_CONTAINER
    .timeContainer    = &g_rootTimeContainer,
    .timeContainerForChildren = &g_rootTimeContainer,
#endif

#ifdef LOSCFG_IPC_CONTAINER
    .ipcContainer = &g_rootIpcContainer,
#endif
};

void InitContainerBundle()
{
#ifdef LOSCFG_UTS_CONTAINER
    InitUtsContainer(&g_rootUtsContainer);
#endif
#ifdef LOSCFG_MNT_CONTAINER
    InitMntContainer(&g_rootMntContainer);
#endif
#ifdef LOSCFG_NET_CONTAINER
    InitNetContainer(&g_rootNetContainer);
#endif
#ifdef LOSCFG_PID_CONTAINER
    InitPidContainer(&g_rootPidContainer);
#endif
#ifdef LOSCFG_TIME_CONTAINER
    InitTimeContainer(&g_rootTimeContainer);
#endif
#ifdef LOSCFG_IPC_CONTAINER
    InitRootIpcContainer(&g_rootIpcContainer);
#endif
}

static inline ContainerBundle *CreateContainerBundle()
{
    ContainerBundle *containerBundle = NULL;

    UINT32 size = sizeof(ContainerBundle);
    containerBundle = LOS_MemAlloc(m_aucSysMem1, size);
    if (containerBundle == NULL) {
        PRINT_ERR("CreateContainerBundle failed to malloc memory.\n");
        return NULL;
    }
#ifdef LOSCFG_MNT_CONTAINER
    containerBundle->mntContainer = NULL;
#endif
#ifdef LOSCFG_PID_CONTAINER
    containerBundle->pidContainer = NULL;
    containerBundle->pidContainerForChildren = NULL;
#endif
#ifdef LOSCFG_UTS_CONTAINER
    containerBundle->utsContainer = NULL;
#endif
#ifdef LOSCFG_NET_CONTAINER
    containerBundle->netContainer = NULL;
#endif
#ifdef LOSCFG_TIME_CONTAINER
    containerBundle->timeContainer = NULL;
    containerBundle->timeContainerForChildren = NULL;
#endif
#ifdef LOSCFG_IPC_CONTAINER
    containerBundle->ipcContainer = NULL;
#endif
    atomic_set(&containerBundle->count, 1);
    return containerBundle;
}

static ContainerBundle *CreateNewContainers(unsigned long flags, LosProcessCB *runProcessCB)
{
    ContainerBundle *newContainerP = NULL;

    newContainerP = CreateContainerBundle();
    if (newContainerP == NULL) {
        PRINT_ERR("newContainerP is NULL.\n");
        return NULL;
    }

#ifdef LOSCFG_UTS_CONTAINER
    newContainerP->utsContainer = HandleUtsContainer(flags, runProcessCB->containerBundle->utsContainer);
    if (newContainerP->utsContainer == NULL) {
        PRINT_ERR("newContainerP->utsContainer is NULL.\n");
        goto out;
    }
#endif

#ifdef LOSCFG_PID_CONTAINER
    newContainerP->pidContainer = HandlePidContainer(flags, runProcessCB->containerBundle->pidContainerForChildren);
    if (newContainerP->pidContainer == NULL) {
        PRINT_ERR("newContainerP->pidContainer is NULL.\n");
        goto out;
    }
    newContainerP->pidContainerForChildren = newContainerP->pidContainer;
    GetPidContainer(newContainerP->pidContainerForChildren);
#endif

#ifdef LOSCFG_MNT_CONTAINER
    newContainerP->mntContainer = HandleMntContainer(flags, runProcessCB->containerBundle->mntContainer);
    if (newContainerP->mntContainer == NULL) {
        PRINT_ERR("newContainerP->mntContainer is NULL.\n");
        goto out;
    }
#endif

#ifdef LOSCFG_NET_CONTAINER
    newContainerP->netContainer = HandleNetContainer(flags, runProcessCB->containerBundle->netContainer);
    if (newContainerP->netContainer == NULL) {
        PRINT_ERR("newContainerP->netContainer is NULL.\n");
        goto out;
    }
#endif

#ifdef LOSCFG_TIME_CONTAINER
    newContainerP->timeContainer = HandleTimeContainer(flags, runProcessCB->containerBundle->timeContainerForChildren);
    if (newContainerP->timeContainer == NULL) {
        PRINT_ERR("newContainerP->timeContainer is NULL.\n");
        goto out;
    }
    newContainerP->timeContainerForChildren = newContainerP->timeContainer;
    (VOID)GetTimeContainer(newContainerP->timeContainerForChildren);
#endif

#ifdef LOSCFG_IPC_CONTAINER
    newContainerP->ipcContainer = HandleIpcContainer(flags, runProcessCB->containerBundle->ipcContainer);
    if (newContainerP->ipcContainer == NULL) {
        PRINT_ERR("newContainerP->ipcContainer is NULL.\n");
        goto out;
    }
#endif

    return newContainerP;
#if ((defined LOSCFG_PID_CONTAINER) || (defined LOSCFG_UTS_CONTAINER) || (defined LOSCFG_MNT_CONTAINER) || \
     (defined LOSCFG_NET_CONTAINER) || (defined LOSCFG_TIME_CONTAINER) || (defined LOSCFG_IPC_CONTAINER))
out:
    FreeContainerBundle(newContainerP);
    return NULL;
#endif
}

ContainerBundle *CopyContainerBundleOnly(LosProcessCB *processCB)
{
    ContainerBundle *newContainerBundle = CreateContainerBundle();
    if (newContainerBundle == NULL) {
        PRINT_ERR("newContainerBundle is NULL.\n");
        return NULL;
    }

#ifdef LOSCFG_UTS_CONTAINER
    GetUtsContainer(processCB->containerBundle->utsContainer);
    newContainerBundle->utsContainer = processCB->containerBundle->utsContainer;
#endif

#ifdef LOSCFG_MNT_CONTAINER
    GetMntContainer(processCB->containerBundle->mntContainer);
    newContainerBundle->mntContainer = processCB->containerBundle->mntContainer;
#endif
#ifdef LOSCFG_PID_CONTAINER
    GetPidContainer(processCB->containerBundle->pidContainer);
    newContainerBundle->pidContainer = processCB->containerBundle->pidContainer;
    GetPidContainer(processCB->containerBundle->pidContainerForChildren);
    newContainerBundle->pidContainerForChildren = processCB->containerBundle->pidContainerForChildren;
#endif

#ifdef LOSCFG_NET_CONTAINER
    GetNetContainer(processCB->containerBundle->netContainer);
    newContainerBundle->netContainer = processCB->containerBundle->netContainer;
#endif
#ifdef LOSCFG_TIME_CONTAINER
    (VOID)GetTimeContainer(processCB->containerBundle->timeContainer);
    newContainerBundle->timeContainer = processCB->containerBundle->timeContainer;
    (VOID)GetTimeContainer(processCB->containerBundle->timeContainerForChildren);
    newContainerBundle->timeContainerForChildren = processCB->containerBundle->timeContainerForChildren;
#endif

#ifdef LOSCFG_IPC_CONTAINER
    GetIpcContainer(processCB->containerBundle->ipcContainer);
    newContainerBundle->ipcContainer = processCB->containerBundle->ipcContainer;
#endif

    return newContainerBundle;
}

/*
 * called from clone.  This now handles copy for ContainerBundle and all
 * namespaces therein.
 */
int CopyContainers(unsigned long flags, LosProcessCB *childProcessCB, LosProcessCB *runProcessCB)
{
    ContainerBundle *newContainer = NULL;
#ifdef LOSCFG_PID_CONTAINER
    INT32 ret;
#endif
    BOOL shouldCreateNewBundle = false;
#ifdef LOSCFG_TIME_CONTAINER
    flags &= ~CLONE_NEWTIME;
#endif
    if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET)))) {
#ifdef LOSCFG_PID_CONTAINER
        if (runProcessCB->containerBundle->pidContainerForChildren != runProcessCB->containerBundle->pidContainer) {
            shouldCreateNewBundle = true;
        }
#endif
#ifdef LOSCFG_TIME_CONTAINER
        if (runProcessCB->containerBundle->timeContainerForChildren != runProcessCB->containerBundle->timeContainer) {
            shouldCreateNewBundle = true;
        }
#endif

    } else {
        shouldCreateNewBundle = true;
    }

    if (!shouldCreateNewBundle) {
        GetContainerBundle(runProcessCB->containerBundle);
        childProcessCB->containerBundle = runProcessCB->containerBundle;
    }
    else {
        newContainer = CreateNewContainers(flags, runProcessCB);
        if (newContainer == NULL) {
            PRINT_ERR("newContainer is NULL.\n");
            return EINVAL;
        }
        childProcessCB->containerBundle = newContainer;
    }
#ifdef LOSCFG_PID_CONTAINER
    ret = PlaceProcessIntoContainers(childProcessCB);
    if (ret != LOS_OK) {
        DerefContainerBundle(childProcessCB->containerBundle);
        return EINVAL;
    }
#endif
    return 0;
}

#ifdef LOSCFG_PID_CONTAINER
int PlaceProcessIntoContainers(LosProcessCB *childProcessCB) {
    return PlaceProcessIntoPidContainer(childProcessCB);
}
#endif

void FreeContainerBundle(ContainerBundle *pContainer)
{
#ifdef LOSCFG_UTS_CONTAINER
    if (pContainer->utsContainer) {
        DerefUtsContainer(pContainer->utsContainer);
    }
#endif

#ifdef LOSCFG_PID_CONTAINER
    if (pContainer->pidContainer) {
        DerefPidContainer(pContainer->pidContainer);
    }

    if (pContainer->pidContainerForChildren) {
        DerefPidContainer(pContainer->pidContainerForChildren);
    }
#endif

#ifdef LOSCFG_MNT_CONTAINER
    if (pContainer->mntContainer) {
        DerefMntContainer(pContainer->mntContainer);
    }
#endif

#ifdef LOSCFG_NET_CONTAINER
    if (pContainer->netContainer) {
        DerefNetContainer(pContainer->netContainer);
    }
#endif

#ifdef LOSCFG_TIME_CONTAINER
    if (pContainer->timeContainer) {
        DerefTimeContainer(pContainer->timeContainer);
    }

    if (pContainer->timeContainerForChildren) {
        DerefTimeContainer(pContainer->timeContainerForChildren);
    }
#endif

#ifdef LOSCFG_IPC_CONTAINER
    if (pContainer->ipcContainer) {
        DerefIpcContainer(&(pContainer->ipcContainer));
    }
#endif

    LOS_MemFree(m_aucSysMem1, pContainer);
}

void ExitProcessContainer(LosProcessCB *task)
{
#ifdef LOSCFG_PID_CONTAINER
    ExitPidContainer(task);
#endif
    ContainerBundle *pContainerBundle = NULL;
    pContainerBundle = task->containerBundle;

    DerefContainerBundle(pContainerBundle);
}

int UnshareContainers(unsigned long flags, ContainerBundle **bundle) {
    if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWTIME | CLONE_NEWIPC))) {
        return LOS_OK;
    }

    LosProcessCB *currProcess = OsCurrProcessGet();
    *bundle = CreateNewContainers(flags, currProcess);

#ifdef LOSCFG_PID_CONTAINER
    if (flags & CLONE_NEWPID) {
        DerefPidContainer((*bundle)->pidContainer);
        (*bundle)->pidContainer = currProcess->containerBundle->pidContainer;
        GetPidContainer((*bundle)->pidContainer);
    }
#endif
#ifdef LOSCFG_TIME_CONTAINER
    if (flags & CLONE_NEWTIME) {
        DerefTimeContainer((*bundle)->timeContainer);
        (*bundle)->timeContainer = currProcess->containerBundle->timeContainer;
        (VOID)GetTimeContainer((*bundle)->timeContainer);
    }
#endif

    return LOS_OK;
}

void AssignContainers(LosProcessCB *process, ContainerBundle *newBundle) {
    UINT32 intSave;
    ContainerBundle *oldBundle = NULL;

    SCHEDULER_LOCK(intSave);
    oldBundle = process->containerBundle;
    process->containerBundle = newBundle;
    SCHEDULER_UNLOCK(intSave);

    if (oldBundle) {
        DerefContainerBundle(oldBundle);
    }
}

void *GetTargetContainer(INT32 fd)
{
    struct Vnode *vnode = GetVnode(fd);
    if (vnode)
    {
        return ProcfsContainerGet(vnode);
    } else {
        return NULL;
    }
}

INT32 GetTargetContainerType(INT32 fd, INT32 type)
{
    struct Vnode *vnode = GetVnode(fd);

    INT32 containerType;

    if (vnode)
    {
        containerType = ProcfsContainerTypeGet(vnode);
        if ((type != 0) && (type != containerType))
        {
            return -1;
        }
        return containerType;

    } else {
        return -1;
    }
}
#endif
